package references

import (
	"fmt"
	"lesson01/instructions/base"
	"lesson01/rtda"
	"lesson01/rtda/heap"
)

type InvokeVirtual struct {
	base.Index16Instruction
}

func (e *InvokeVirtual) Execute(frame *rtda.Frame) {
	currentClass := frame.Method().Class()
	cp := currentClass.ConstantPool()
	methodRef := cp.GetConstant(e.Index).(*heap.MethodRef)
	resolveMethod := methodRef.ResolveMethod()
	if resolveMethod.IsStatic() {
		//它通常发生在类加载过程中，当 JVM 遇到类的定义与预期不一致时抛出
		panic("java.lang.IncompatibleClassChangeError")
	}

	ref := frame.OperandStack().GetRefFromTop(resolveMethod.ArgSlotCount() - 1)
	if ref == nil {
		if methodRef.Name() == "println" {
			_printLn(frame.OperandStack(), methodRef.Descriptor())
			return
		}

		panic("java.lang.NullPointerException")
	}

	if resolveMethod.IsProtected() &&
		resolveMethod.Class().IsSubClassOf(currentClass) &&
		resolveMethod.Class().GetPackageName() != currentClass.GetPackageName() &&
		ref.Class() != currentClass &&
		!ref.Class().IsSubClassOf(currentClass) {
		// 试图访问一个类、接口、字段或方法，但访问权限不足
		panic("java.lang.IllegalAccessError")
	}

	//从对象的类中查找真正要调用的方法。如果找不到方法，或者
	//找到的是抽象方法，则需要抛出AbstractMethodError异常，否则一
	//切正常，调用方法
	methodToBeInvoked := heap.LookupMethodInClass(ref.Class(),
		methodRef.Name(), methodRef.Descriptor())

	if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
		panic("java.lang.AbstractMethodError")
	}

	base.InvokeMethod(frame, methodToBeInvoked)

}

// hack!
func _printLn(stack *rtda.OperandStack, description string) {
	switch description {
	case "(Z)V":
		fmt.Printf("%v\n", stack.PopInt() != 0)
	case "(C)V":
		fmt.Printf("%c\n", stack.PopInt())
	case "(I)V", "(B)V", "(S)V":
		fmt.Printf("%v\n", stack.PopInt())
	case "(F)V":
		fmt.Printf("%v\n", stack.PopFloat())
	case "(J)V":
		fmt.Printf("%v\n", stack.PopLong())
	case "(D)V":
		fmt.Printf("%v\n", stack.PopDouble())
	case "(Ljava/lang/String;)V":
		jStr := stack.PopRef()
		goStr := heap.GoString(jStr)
		fmt.Println(goStr)
	default:
		panic("println: " + description)
	}
	stack.PopRef()
}
